home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Linux / Kubuntu 8.10 / kubuntu-8.10-desktop-i386.iso / casper / filesystem.squashfs / usr / share / pyshared / launchpadbugs / tasksbase.py < prev    next >
Encoding:
Python Source  |  2008-08-27  |  14.8 KB  |  373 lines

  1.  
  2. from lphelper import change_obj, product, user, LateBindingProperty
  3. import re
  4. import urlparse
  5.  
  6.  
  7. class LPTask(object):
  8.     """ The 'LPTask'-object represents on task of a bugreport
  9.     
  10.     * editable attributes:
  11.         .sourcepackage: lp-name of a package/project
  12.         .status: valid lp-status
  13.         .importance: valid lp-importance (if the user is not permitted to
  14.             change 'importance' an 'IOError' will be raised
  15.         .assignee: lp-login of an user/group
  16.         .milestone: value must be in '.valid_milestones'
  17.         
  18.     * read-only attributes:
  19.         .affects, .target, .valid_milestones
  20.         
  21.     TODO: * rename 'Info' into 'Task'
  22.     
  23.     Arguments
  24.         affects (h,t,rw)
  25.         status (h,t,rw)
  26.         importance (h,t,rw)
  27.         assignee (h,t,rw)
  28.         current
  29.         editurl
  30.         type
  31.         milestone
  32.         available_milestone
  33.         lock_importance
  34.         targeted_to
  35.         remote
  36.         editlock
  37.         edit_fields
  38.         connection
  39.         
  40.     Attributes:
  41.         sourcepackage
  42.         status
  43.         importance
  44.         assignee
  45.         milestone
  46.         targeted_to
  47.         remote
  48.         affects
  49.         target
  50.         valid_milestones
  51.         component
  52.     
  53.     """
  54.     def __init__(self, value_dict={}):
  55.     
  56.                 #(self, affects, status, importance, assignee, current,
  57.                     #editurl, type, milestone, available_milestone,
  58.                     #lock_importance, targeted_to, remote, editlock,
  59.                     #edit_fields, connection):
  60.         self._connection = value_dict.get("connection", None)
  61.         self._editlock = value_dict.get("editlock", True)
  62.         self._edit_fields = value_dict.get("edit_fields", set())
  63.         self._lock_importance = value_dict.get("lock_importance", False)
  64.         self._target = None
  65.         self._affects = value_dict.get("affects", product(None))
  66.         temp = self._affects.split(" ")
  67.         #FIXME: does not work .sourcepackage is not None, maybe needs .lower()
  68.         if temp[0] == "Ubuntu":
  69.             self._sourcepackage = None
  70.         else:
  71.             self._sourcepackage = temp[0]
  72.         r = re.match(r'^.*\((.*)\)$', self._affects.longname)
  73.         if r:
  74.             self._target = r.group(1).lower()
  75.         self._status = value_dict.get("status", None)
  76.         self._importance = value_dict.get("importance", None)
  77.         self._assignee = value_dict.get("assignee", user(None))
  78.         self._current = value_dict.get("current", None)
  79.         self._editurl = value_dict.get("editurl", None)
  80.         self._type = value_dict.get("type", None)
  81.         self._milestone = value_dict.get("milestone", None)
  82.         self._available_milestone = value_dict.get("available_milestone", {})
  83.         self._targeted_to = value_dict.get("targeted_to", None)
  84.         self._remote = value_dict.get("remote", None)
  85.         
  86.         #"date-created", "date-confirmed", "date-assigned", "date-inprogress", "date-closed"
  87.         self._date_created = value_dict.get("date-created", None)
  88.         self._date_confirmed = value_dict.get("date-confirmed", None)
  89.         self._date_assigned = value_dict.get("date-assigned", None)
  90.         self._date_inprogress = value_dict.get("date-inprogress", None)
  91.         self._date_closed = value_dict.get("date-closed", None)
  92.         self._date_left_new = value_dict.get("date-left-new", None)
  93.         self._date_incomplete = value_dict.get("date-incomplete", None)
  94.         self._date_triaged = value_dict.get("date-triaged", None)
  95.         self._date_fix_committed = value_dict.get("date-fix-committed", None)
  96.         self._date_fix_released = value_dict.get("date-fix-released", None)
  97.         
  98.         #user==reporter
  99.         self._user = value_dict.get("reporter", user(None))
  100.         
  101.         #"component"
  102.         self._component = value_dict.get("component", None)
  103.  
  104.         self._cache = {}        
  105.     
  106.     def __str__(self):
  107.         targeted_to = (self._targeted_to and " (%s)" %self._targeted_to) or ""
  108.         remote = (self._remote and " (remote)") or ""
  109.         return "[%s%s%s: %s/%s]" % (self.affects.longname or self.affects,
  110.                     targeted_to, remote, self.status, self.importance)
  111.         
  112.     def __repr__(self):
  113.         targeted_to = (self._targeted_to and " (%s)" %self._targeted_to) or ""
  114.         remote = (self._remote and " (remote)") or ""
  115.         try:
  116.             number = (not self._editlock and self._editurl.split("/")[-2]) or self._editurl.split("/")[-1]
  117.         except AttributeError:
  118.             number = "unknown"
  119.         return "<Info of '%s%s%s (#%s)'>" %(self.affects.longname or self.affects,
  120.                     targeted_to, remote, number)
  121.             
  122.     def is_current(self, current_task_tupel):
  123.         if current_task_tupel == (self.target, self.targeted_to, self.sourcepackage):
  124.             return True
  125.         return False
  126.         
  127.                     
  128.     def get_date_created(self):
  129.         raise NotImplementedError, 'this method must be implemented by a concrete subclass'
  130.     date_created = LateBindingProperty(get_date_created)
  131.     
  132.     def get_date_confirmed(self):
  133.         raise NotImplementedError, 'this method must be implemented by a concrete subclass'
  134.     date_confirmed = LateBindingProperty(get_date_confirmed)
  135.     
  136.     def get_date_assigned(self):
  137.         raise NotImplementedError, 'this method must be implemented by a concrete subclass'
  138.     date_assigned = LateBindingProperty(get_date_assigned)
  139.     
  140.     def get_date_inprogress(self):
  141.         raise NotImplementedError, 'this method must be implemented by a concrete subclass'
  142.     date_inprogress = LateBindingProperty(get_date_inprogress)
  143.     
  144.     def get_date_closed(self):
  145.         raise NotImplementedError, 'this method must be implemented by a concrete subclass'
  146.     date_closed = LateBindingProperty(get_date_closed)
  147.     
  148.     def get_date_left_new(self):
  149.         raise NotImplementedError, 'this method must be implemented by a concrete subclass'
  150.     date_left_new = LateBindingProperty(get_date_left_new)
  151.     
  152.     def get_date_incomplete(self):
  153.         raise NotImplementedError, 'this method must be implemented by a concrete subclass'
  154.     date_incomplete = LateBindingProperty(get_date_incomplete)
  155.  
  156.     def get_date_triaged(self):
  157.         raise NotImplementedError, 'this method must be implemented by a concrete subclass'
  158.     date_triaged = LateBindingProperty(get_date_triaged)
  159.  
  160.     def get_date_fix_committed(self):
  161.         raise NotImplementedError, 'this method must be implemented by a concrete subclass'
  162.     date_fix_committed = LateBindingProperty(get_date_fix_committed)
  163.  
  164.     def get_date_fix_released(self):
  165.         raise NotImplementedError, 'this method must be implemented by a concrete subclass'
  166.     date_fix_released = LateBindingProperty(get_date_fix_released)
  167.     
  168.     def get_user(self):
  169.         raise NotImplementedError, 'this method must be implemented by a concrete subclass'
  170.     user = LateBindingProperty(get_user)
  171.  
  172.     def get_component(self):
  173.         raise NotImplementedError, 'this method must be implemented by a concrete subclass'
  174.     component = LateBindingProperty(get_component)
  175.                      
  176.     def get_targeted_to(self):
  177.         #raise NotImplementedError, 'this method must be implemented by a concrete subclass'
  178.         return self._targeted_to
  179.     targeted_to = LateBindingProperty(get_targeted_to)
  180.         
  181.     def get_remote(self):
  182.         #raise NotImplementedError, 'this method must be implemented by a concrete subclass'
  183.         return self._remote
  184.     remote = LateBindingProperty(get_remote)
  185.                      
  186.     def get_affects(self):
  187.         #raise NotImplementedError, 'this method must be implemented by a concrete subclass'
  188.         return self._affects
  189.     affects = LateBindingProperty(get_affects)
  190.         
  191.     def get_target(self):
  192.         #raise NotImplementedError, 'this method must be implemented by a concrete subclass'
  193.         return self._target
  194.     target = LateBindingProperty(get_target)
  195.         
  196.     def get_sourcepackage(self):
  197.         return self._sourcepackage
  198.         
  199.     def set_sourcepackage(self, package):
  200.         raise NotImplementedError, 'this method must be implemented by a concrete subclass'
  201.     sourcepackage = LateBindingProperty(get_sourcepackage, set_sourcepackage, doc="sourcepackage of a bug")
  202.  
  203.     def get_assignee(self):
  204.         #raise NotImplementedError, 'this method must be implemented by a concrete subclass'
  205.         return self._assignee
  206.         
  207.     def set_assignee(self, lplogin):
  208.         raise NotImplementedError, 'this method must be implemented by a concrete subclass'
  209.     assignee = LateBindingProperty(get_assignee, set_assignee, doc="assignee to a bugreport")
  210.     
  211.     def get_status(self):
  212.         #raise NotImplementedError, 'this method must be implemented by a concrete subclass'
  213.         return self._status
  214.         
  215.     def set_status(self, status):
  216.         raise NotImplementedError, 'this method must be implemented by a concrete subclass'
  217.     status = LateBindingProperty(get_status, set_status, doc="status of a bugreport")
  218.                               
  219.         
  220.     def get_importance(self):
  221.         #raise NotImplementedError, 'this method must be implemented by a concrete subclass'
  222.         return self._importance
  223.         
  224.     def set_importance(self, importance):
  225.         raise NotImplementedError, 'this method must be implemented by a concrete subclass'
  226.     importance = LateBindingProperty(get_importance, set_importance, doc="importance of a bugreport")
  227.     
  228.     def get_valid_milestones(self):
  229.         #raise NotImplementedError, 'this method must be implemented by a concrete subclass'
  230.         return self._available_milestone
  231.     valid_milestones = LateBindingProperty(get_valid_milestones)    
  232.         
  233.     def get_milestone(self):
  234.         #raise NotImplementedError, 'this method must be implemented by a concrete subclass'
  235.         return self._milestone
  236.         
  237.     def set_milestone(self, milestone):
  238.         raise NotImplementedError, 'this method must be implemented by a concrete subclass'
  239.     milestone = LateBindingProperty(get_milestone, set_milestone, doc="milestone of a bugreport")
  240.     
  241.         
  242.     def get_changed(self):
  243.         changed = set()
  244.         for k in self._cache:
  245.             if self._cache[k] != getattr(self, k):
  246.                 changed.add(k)
  247.         return frozenset(changed)
  248.     changed = property(get_changed, doc="get a list of changed attributes")
  249.     
  250.     def commit(self, force_changes=False, ignore_lp_errors=True):
  251.         """ Commits the local changes to launchpad.net
  252.         
  253.         * force_changes: general argument, has not effect in this case
  254.         * ignore_lp_errors: if the user tries to commit invalid data to launchpad,
  255.             launchpad returns an error-page. If 'ignore_lp_errors=False' Info.commit()
  256.             will raise an 'ValueError' in this case, otherwise ignore this
  257.             and leave the bugreport in launchpad unchanged (default=True)
  258.         """
  259.         raise NotImplementedError, 'this method must be implemented by a concrete subclass'
  260.  
  261.  
  262.  
  263. class LPTasks(list):
  264.     """ The 'LPTasks'-object represents the tasks related to a bugreport
  265.         
  266.     * read-only attributes:
  267.         .current: returns the highlighted Info-object of the bugreport
  268.     
  269.     TODO:  * rename 'InfoTable' into 'TaskTable'
  270.            * allow adding of tasks (Also affects upstream/Also affects distribution)
  271.            * does current/tracked work as expected?
  272.            * remote: parse editable values
  273.     """
  274.     def __init__(self, conf_data={}):
  275.         self._current = None 
  276.         self.parsed = False
  277.         self._url = conf_data.get("url","")
  278.         self._xml = conf_data.get("xml","")
  279.         self._connection = conf_data.get("connection",None)
  280.         self.__added = []
  281.         list.__init__(self)
  282.         
  283.     def __repr__(self):
  284.         return "<InfoTable>"
  285.         
  286.     def __str__(self):
  287.         x = (len(self) > 1 and "[%s]") or "%s"
  288.         return x %",".join(str(i) for i in self)
  289.         
  290.     def addTask(self, task):
  291.         if not isinstance(task, LPTask):
  292.             raise TypeError, "task has to be instance of 'LPTask'"
  293.         self.__added.append(task)
  294.         self.append(task)
  295.         
  296.     def remove(self, item):
  297.         if not item in self.__added:
  298.             raise RuntimeError, "LP does not allow to remove Tasks"
  299.         list.remove(self, item)
  300.  
  301.     def parse(self):
  302.         pass
  303.         
  304.     re_url = re.compile(r"^(/([a-z0-9-]+)/([a-z0-9-]+)?/?\+source)?/([a-z0-9-]+)/\+bug/.*$")
  305.         
  306.     @staticmethod
  307.     def current_from_url(url):
  308.         """ returns (<distibution>,<release>,<product>) or False """
  309.         x = LPTasks.re_url.search(urlparse.urlsplit(url)[2])
  310.         if x:
  311.             return x.groups()[1:]
  312.         else:
  313.             return False
  314.             
  315.     re_listurl = re.compile(r"^(/([a-z0-9\-\.]+)/([a-z0-9\-\.]+)?/?\+source)?/([a-z0-9\-\.]+)/?(\+bugs)?$")
  316.         
  317.     @staticmethod
  318.     def current_from_listurl(url):
  319.         """ returns (<distibution>,<release>,<product>) or False """
  320.         x = LPTasks.re_listurl.search(urlparse.urlsplit(url)[2])
  321.         if x:
  322.             return x.groups()[1:-1]
  323.         else:
  324.             return False      
  325.         
  326.     def get_current(self):
  327.         if self._current == None:
  328.             raise AttributeError, "There is no row of the info-table linked to this bugreport (%s)" %self._url
  329.         assert isinstance(self._current, int), "No task related to %s?" %self._url
  330.         return self[self._current]
  331.     current = property(get_current, doc= "get the info-object for the current bug")
  332.     
  333.     def has_target(self, target):
  334.         if not target:
  335.             return None in [i.target for i in self]
  336.         target = target.lower()
  337.         for i in self:
  338.             if i.target:
  339.                 if i.target.lower() == target:
  340.                     return True
  341.         return False
  342.         
  343.     @property
  344.     def changed(self):
  345.         ret = list()
  346.         for i in self:
  347.             if i in self.__added:
  348.                 ret.append(change_obj(i, "added"))
  349.             elif i.changed:
  350.                 ret.append(change_obj(i))
  351.         return ret
  352.         
  353.     def commit(self, force_changes=False, ignore_lp_errors=True):
  354.         """ delegates commit() to each changed element """
  355.         for i in self.changed:
  356.             if i.action == "added":
  357.                 self._LP_create_task(i, force_changes, ignore_lp_errors)
  358.             else:
  359.                 i.component.commit(force_changes, ignore_lp_errors)
  360.                 
  361.     def _LP_create_task(task, force_changes, ignore_lp_errors):
  362.         raise NotImplementedError
  363.             
  364.  
  365. #some test-cases
  366. if __name__ == '__main__':
  367.     for i in (  "https://launchpad.net/bugs/12345",
  368.                 "https://bugs.edge.launchpad.net/bughelper/+bug/152499",
  369.                 "https://bugs.edge.launchpad.net/ubuntu/+source/bughelper/+bug/107735",
  370.                 "https://bugs.edge.launchpad.net/ubuntu/feisty/+source/bughelper/+bug/109213",
  371.                 "https://bugs.edge.launchpad.net/ubuntu/+source/python-launchpad-bugs/+bug/153842"):
  372.         print i, "\t\t", LPTasks.current_from_url(i)
  373.